/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package MiddleLayer;

import MiddleLayer.OtherClass.CommandStruct;
import NetworkManager.MultiThreadChatServer;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

/**
 *
 * @author ThanhTri
 * Lớp xử lý các sự kiện ở Server. Bao gồm:
 * - Tạo kết nối client - server
 * - Gửi và phản hồi thông tin với client
 * - Kiểm tra trạng thái luật của trò chơi
 */
public class ServerProcessing implements Observer{

    
    public boolean closed = false;
    MultiThreadChatServer server = null;
    int port_number = 2223;

    List<Cards> lstPlayerCards = null;
    List<Integer> lstPassCard = new ArrayList<Integer>(); //Đánh dấu người chơi pass
    List<Integer> lstPlayCard = new ArrayList<Integer>(); //Đánh dấu dùng trong 1 lần ra bài.
    List<Integer> lstMarks = new ArrayList<Integer>(); //danh sách điểm tính tới thời điểm hiện tại

    int iFirstPlayerInRound = -1;//Người đi đầu tiên trong round đó
    //int iCurrentPlayer = 0;
    int iNumOfPlayers = -1;
    int iNumOfCards = -1;//thay đổi tùy theo só người chơi
    int iCurrPlayer = -1;//Người chơi hiện tại mới gửi dữ liệu lên server
    int iOrderOfDeal = 0;//Cho biết hiện tại là ván bài thứ mấy
    int iOrderOfRound = 0;//Cho biết hiện tại là lượt thứ mấy của ván này rồi, tối đa số nước đi bằng số quân bài


    /*
     * Khởi tạo server và chờ kết nối từ bên ngoài
     * Đồng thời bắt đầu gửi dữ liệu luôn
     */
    public void initServer()
    {
        this.server = new MultiThreadChatServer();
        this.server.addObserver(this);
        
        server.initServer(port_number);

        //this.startGame();
        
        //this.server.disconnectClients();

    }

    public void startGame()
    {
        this.iNumOfPlayers = this.server.getNumOfClient();
        //Ngưng nhận kết nối
        this.server.setNotAcceptConnect();

        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
        {
            this.lstPlayerCards.add(new Cards());
            this.lstMarks.add( new Integer(0) );
        }

        this.generateCards();

        //Send bài qua cho client sau khi tạo xong
        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
            this.server.sendDataToThreadNumber(iPlayer, new CommandStruct(this.lstPlayerCards.get(iPlayer)));

//        while(!this.closed)
//        {
//        }
    }

    /**
     * Khởi tạo random các lá bài cho các đội chơi
     * Đồng thời khởi tạo các giá trị khác để server handle trò chơi
     */
    private void generateCards()
    {
        List<Integer> lstRandomCard = null;
        int iRandomCard = 0;
        this.iNumOfCards = 0;//thay đổi tùy theo só người chơi

        for(int i = 0; i < 52; ++i)
        {
            lstRandomCard.add(0);
        }

        switch(this.iNumOfPlayers)
        {
            case 4:
                iNumOfCards = 13;

                for(int player = 0; player < iNumOfPlayers; ++ player)
                {
                    Cards cards = this.lstPlayerCards.get(player);

                    //trường hợp chia cho các người chơi khác trước
                    if(player < iNumOfPlayers - 1)
                    {
                        for(int iCard = 0; iCard < iNumOfCards; ++iCard)
                        {
                            do{
                                iRandomCard = (int) ( Math.random()*51 + 0.5 );
                            }while(lstRandomCard.get(iRandomCard) != 0);

                            //Đánh dấu đã sử dụng lá bài này
                            lstRandomCard.set(iRandomCard, 1);

                            cards.addCard(iRandomCard);
                        }
                    }
                    else//Thêm các lá bài còn lại cho người chơi cuối cùng
                    {
                        for(int i = 0; i < lstRandomCard.size(); ++i)
                        {
                            if(lstRandomCard.get(i) != 1)
                                cards.addCard(i);
                        }

                    }
                }
                break;

                case 3:
                    iNumOfCards = 17;
                    for(int player = 0; player < iNumOfPlayers; ++ player)
                    {
                        Cards cards = this.lstPlayerCards.get(player);

                        if(player < iNumOfPlayers - 1)
                        {                            
                            for (int iCard = 0; iCard < iNumOfCards; ++iCard)
                            {
                                do{
                                    iRandomCard = (int) ( Math.random()*51 + 0.5 );
                                }while(lstRandomCard.get(iRandomCard) != 0 && !Card.isTwoOfDiamonds(iRandomCard));

                                //Đánh dấu đã sử dụng lá bài này
                                lstRandomCard.set(iRandomCard, 1);

                                cards.addCard(iRandomCard);
                            }
                        }
                        else//Thêm các lá bài còn lại cho người chơi cuối cùng
                        {
                            for(int i = 0; i < lstRandomCard.size(); ++i)
                            {
                                int id = lstRandomCard.get(i);
                                if(id != 1 && !Card.isTwoOfDiamonds(id))
                                    cards.addCard(i);
                            }
                        }

                    }
                    break;

                case 2:
                    iNumOfCards = 26;
                    Cards cards = this.lstPlayerCards.get(0);

                    for(int iCard = 0; iCard < iNumOfCards; ++iCard)
                    {
                        do{
                            iRandomCard = (int) ( Math.random()*51 + 0.5 );
                        }while(lstRandomCard.get(iRandomCard) != 0);

                        //Đánh dấu đã sử dụng lá bài này
                        lstRandomCard.set(iRandomCard, 1);

                        cards.addCard(iRandomCard);
                    }
                    
                    cards = this.lstPlayerCards.get(1);
                    
                    for(int i = 0; i < lstRandomCard.size(); ++i)
                    {
                        int id = lstRandomCard.get(i);
                        if(id != 1)
                            cards.addCard(i);
                    }
                    
                    break;

        };
        
        //Khởi tạo giá trị cho 2 lstPassCard và lstPlayCard
        setValueForLstCard(0);

    }

    void setValueForLstCard(int iValue)
    {
        this.lstPassCard.clear();
        this.lstPlayCard.clear();
        for(int i = 0; i < this.iNumOfPlayers; ++i)
        {
            this.lstPassCard.add(new Integer(iValue));
            this.lstPlayCard.add(new Integer(iValue));
        }
    }

    boolean isAllLstHasTheValue(int iValue, List<Integer> lst)
    {
        boolean bResult = true;
        for(int i = 0; i < this.iNumOfPlayers; ++i)
        {
            if( (Integer) lst.get(i) != iValue)
                bResult = false;
        }
        return bResult;
    }

    /**
     * Người chơi được nhận thêm các lá bài trong lượt PASS
     * @param iPlayer
     * @param cmdStruct
     */
    void addCardsToPlayer(int iPlayer, CommandStruct cmdStruct)
    {
        Cards cards = this.lstPlayerCards.get(iPlayer);
        List<Integer> lstIDs = cmdStruct.getPassIDCards();
        for(int i = 0; i < lstIDs.size(); ++i)
        {
            cards.addCard(lstIDs.get(i));
        }
    }

    /**
     * Người chơi được nhận thêm các lá bài trong lượt PASS
     * @param iPlayer
     * @param cmdStruct
     */
    void removeCardsFromPlayer(int iPlayer, CommandStruct cmdStruct)
    {
        Cards cards = this.lstPlayerCards.get(iPlayer);
        List<Integer> lstIDs = cmdStruct.getPassIDCards();
        for(int i = 0; i < lstIDs.size(); ++i)
        {
            cards.removeCard(lstIDs.get(i));
        }
    }

    /*
     * Có dữ liệu tới
     */
    public void update(Observable o, Object arg) {

        //Đầu tiên cần biết dữ liệu đó là từ thằng nào
        int iDataFromClientNumber = (Integer)arg;
        
        //Xử lý dữ liệu nhận từ client này
        this.iCurrPlayer = iDataFromClientNumber /10 - 1;

        if(iDataFromClientNumber % 10 == 0)//Dữ liệu chat
            processMessage(this.server.clientChatReceiver[this.iCurrPlayer]);
        else
            processMessage(this.server.clientCMDReceiver[this.iCurrPlayer]);
    }

    /*
     * Tập trung các thông điệp về để xử lý
     */
    private void processMessage(CommandStruct cmdStruct)
    {
        switch(cmdStruct.getMessageType())
        {
            case NewPlayer:
                CommandStruct welcomeNewPlayer = new CommandStruct(
                        CommandStruct.MessageType.Chat, //Thông điệp được gửi sẽ là thông điệp chat
                        "Chào mừng người chơi " + cmdStruct.getContentOfMessage());
                this.server.sendDataToAllThread(welcomeNewPlayer);
                break;

            case Chat:
                this.server.sendDataToOtherThread(this.server.clientChatReceiver[iCurrPlayer], iCurrPlayer);
                break;

            case Pass:
                this.lstPassCard.set(this.iCurrPlayer, new Integer(1));

                //Nếu đã nhận được đầy đủ thông tin đổi bài từ các client
                if(isAllLstHasTheValue(1, this.lstPassCard))
                {
                    //PASS tùy theo số lượng người chơi
                    // <editor-fold defaultstate="collapsed" desc="Pass bài trên server tùy theo số lượng người chơi và số lượt đã chơi">
                    switch (this.iNumOfPlayers) {
                        case 2:
                            if (this.iOrderOfDeal % 2 == 0)//Lượt chẵn: Đổi bài cho nhau
                            {
                                this.removeCardsFromPlayer(0, this.server.clientCMDReceiver[0]);
                                this.addCardsToPlayer(1, this.server.clientCMDReceiver[0]);
                                this.removeCardsFromPlayer(1, this.server.clientCMDReceiver[1]);
                                this.addCardsToPlayer(0, this.server.clientCMDReceiver[1]);
                            } else//Lượt lẻ: Không đổi bài
                            {
                            }
                            break;
                        case 3:
                            switch (this.iOrderOfDeal % 3) {
                                case 0://Chia về bên trái
                                    this.removeCardsFromPlayer(0, this.server.clientCMDReceiver[0]);
                                    this.addCardsToPlayer(0, this.server.clientCMDReceiver[2]);
                                    this.removeCardsFromPlayer(1, this.server.clientCMDReceiver[1]);
                                    this.addCardsToPlayer(1, this.server.clientCMDReceiver[0]);
                                    this.removeCardsFromPlayer(2, this.server.clientCMDReceiver[2]);
                                    this.addCardsToPlayer(2, this.server.clientCMDReceiver[1]);
                                    break;
                                case 1://Chia về bên phải
                                    this.removeCardsFromPlayer(0, this.server.clientCMDReceiver[0]);
                                    this.addCardsToPlayer(0, this.server.clientCMDReceiver[1]);
                                    this.removeCardsFromPlayer(1, this.server.clientCMDReceiver[1]);
                                    this.addCardsToPlayer(1, this.server.clientCMDReceiver[2]);
                                    this.removeCardsFromPlayer(2, this.server.clientCMDReceiver[2]);
                                    this.addCardsToPlayer(2, this.server.clientCMDReceiver[0]);
                                    break;
                                case 2://Không làm gì cả
                                    break;
                            }
                            break;
                        case 4:
                            switch (this.iOrderOfDeal % 4) {
                                case 0://chia qua bên tay trái
                                    for (int iPlayer = 0; iPlayer < this.iNumOfPlayers - 1; ++iPlayer) {
                                        this.removeCardsFromPlayer(iPlayer, this.server.clientCMDReceiver[iPlayer]);
                                        this.addCardsToPlayer(iPlayer + 1, this.server.clientCMDReceiver[iPlayer]);
                                    }
                                    this.removeCardsFromPlayer(3, this.server.clientCMDReceiver[3]);
                                    this.addCardsToPlayer(0, this.server.clientCMDReceiver[3]);
                                    break;
                                case 1://chia qua bên tay phải
                                    for (int iPlayer = 1; iPlayer < this.iNumOfPlayers; ++iPlayer) {
                                        this.removeCardsFromPlayer(iPlayer, this.server.clientCMDReceiver[iPlayer]);
                                        this.addCardsToPlayer(iPlayer - 1, this.server.clientCMDReceiver[iPlayer]);
                                    }
                                    this.removeCardsFromPlayer(0, this.server.clientCMDReceiver[0]);
                                    this.addCardsToPlayer(3, this.server.clientCMDReceiver[0]);
                                    break;
                                case 2://chia người đối diện.
                                    for (int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer) {
                                        this.removeCardsFromPlayer(iPlayer, this.server.clientCMDReceiver[iPlayer]);
                                    }
                                    this.addCardsToPlayer(0, this.server.clientCMDReceiver[2]);
                                    this.addCardsToPlayer(1, this.server.clientCMDReceiver[3]);
                                    this.addCardsToPlayer(2, this.server.clientCMDReceiver[0]);
                                    this.addCardsToPlayer(3, this.server.clientCMDReceiver[1]);
                                    break;
                                case 3://Không làm gì cả
                                    break;
                            }
                            break;
                    }// </editor-fold>

                    //Cập nhật lại trạng thái các lá bài cho client.
                    for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
                    {
                        this.server.sendDataToThreadNumber(iPlayer, new CommandStruct(this.lstPlayerCards.get(iPlayer)));
                    }
                }
                break;

            case StartPlaying://Được nhận từ client nào đó không cần biết. Mặc định là chỉ client tạo server mới được thực hiện chức năng này
                this.startGame();
                break;
                
            case PlayCard://Trường hợp người chơi chọn lá bài để đi
                if(iFirstPlayerInRound != -1)
                    iFirstPlayerInRound = this.iCurrPlayer;

                this.lstPlayCard.set(this.iCurrPlayer, 1);

                //Đẩy thông tin cho các client khác biết player này đã đi nước này
                this.server.sendDataToOtherThread(cmdStruct, iCurrPlayer);

                //server nhận đủ thông tin từ phía client
                //Bắt đầu tính điểm và gửi lại thông tin cập nhật bộ bài cho client nếu cần
                if(isAllLstHasTheValue(1, this.lstPlayCard))
                {
                    this.iOrderOfRound++;

                    //Kết thúc 1 lượt đi, cập nhật danh sách lá bài của các client đã đi
                    if (this.iOrderOfRound < this.iNumOfCards) {

                        //Tính xem ai là người ăn hết các lá bài (Người điểm cao nhất)
                        //Cho người đó đi trước
                        int iNextFirstPlayer = checkWhenEndRound();
                        
                        //Gửi thông báo đến cho client để bắt đầu lượt đấu mới(vẫn trong vòng đấu này)
                        this.server.sendDataToAllThread(
                                new CommandStruct(
                                        CommandStruct.MessageType.NewRound,
                                        Integer.toString(iNextFirstPlayer)
                                                 )
                                                        );
                        iFirstPlayerInRound = -1;
                    }
                    else
                    {
                        //Tính điểm
                        calScoreWhenEndDeal();

                        int idWinnerPlayer = this.findTheWinner();

                        if(idWinnerPlayer == -1)
                        {
                            //Gửi thông báo đến cho client để bắt đầu vòng đấu mới
                            this.server.sendDataToAllThread(new CommandStruct(CommandStruct.MessageType.NewDeal, ""));
                            this.iOrderOfDeal++;
                            this.iOrderOfRound = 0;
                            this.iFirstPlayerInRound = -1;

                            //gọi lại hàm generateCard để phân phối lại các quân bài cho phía client
                            this.generateCards();

                            for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
                                this.server.sendDataToThreadNumber(iPlayer, new CommandStruct(this.lstPlayerCards.get(iPlayer)));

                            //this.processMessage(new CommandStruct(CommandStruct.MessageType.StartPlaying, ""));
                        }
                        else
                        {
                            //Gửi thông báo chúc mừng đến cho client
                            this.server.sendDataToAllThread(new CommandStruct(CommandStruct.MessageType.Chat, "Hết game."));
                        }
                    }
                }
                break;
            case Quit:
                this.closed = true;
                break;
        }
    }

    /**
     * Được gọi để cập nhật lại điểm số sau mỗi vòng thi đấu (vòng thì đấu nhiều lượt)
     */
    private void calScoreWhenEndDeal()
    {
        //Tính điểm tổng kết xem 100 chưa?
        int iIsShootingTheMoon = -1;
        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
        {
            Cards cards = this.lstPlayerCards.get(iPlayer);

            //Kiểm tra trường hợp shooting the moon
            if(cards.isShootingTheMoon())
            {
                iIsShootingTheMoon = iPlayer;
            }
        }

        //Có shooting the moon
        if(iIsShootingTheMoon > -1)
        {
            for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
            {
                int iScore = this.lstMarks.get(iPlayer);

                //Người chơi shooting the moon + 0
                if(iIsShootingTheMoon == iPlayer)
                    iScore += 0;
                else//Không thì cộng tận 26 điểm
                    iScore += 26;

                this.lstMarks.set(iPlayer, iScore);
            }
        }
        else
        {
            for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
            {
                Cards cards = this.lstPlayerCards.get(iPlayer);
                int iScore = this.lstMarks.get(iPlayer);
                iScore += cards.getScore();
                this.lstMarks.set(iPlayer, iScore);
            }
        }
    }

    /**
     *
     * @return
     * Trả về id của client là người chiến thắng:
     * - Thắng khi có số điểm nhỏ nhất
     */
    private int findTheWinner()
    {
        int idPlayer = -1;
        boolean bIsOver100 = false;
        int iSmallestScore = 200;

        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
        {
            int score = (Integer) this.lstMarks.get(iPlayer);

            if(score > 100)
                bIsOver100 = true;
            if(score < iSmallestScore)
            {
                iSmallestScore = score;
                idPlayer = iPlayer;
            }
        }

        //Chưa có người nào quá 100 điểm thì chơi tiếp
        idPlayer = bIsOver100 ? idPlayer : -1;
        return idPlayer;
    }

    /**
     * Tính toán ai sẽ ăn được quân bài khi một lượt kết thúc (vòng có nhiều lượt)
     * @return
     * Người sẽ đi nước đầu tiên ở lượt tiếp theo
     */
    private int checkWhenEndRound()
    {
        List<Card> lstCardInRound = new ArrayList<Card>();

        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
        {
            lstCardInRound.add(new Card(Integer.parseInt(this.server.clientCMDReceiver[iPlayer].getContentOfMessage())));
        }

        int iResult = this.iFirstPlayerInRound;
        Card firstCard = lstCardInRound.get(iResult);
        Card otherCard = null;
        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
        {
            if(iResult != iPlayer)
            {
                otherCard = lstCardInRound.get(iPlayer);

                if(firstCard.isSameSuit(otherCard.getId()))//Có cùng suite hay không
                {
                    if(firstCard.getId() < otherCard.getId())//Kiểm tra xem giá trị lá bài có nhỏ hơn hay không
                    {
                        iResult = iPlayer;
                    }
                }
            }

        }

        //Thêm tất cả các lá bài vào cho client này
        Cards cards = this.lstPlayerCards.get(iResult);
        for(int iCard = 0; iCard < lstCardInRound.size(); ++iCard)
        {
            cards.addWinCard(lstCardInRound.get(iCard).getId());
        }

        //Loại bỏ các lá bài này ra khỏi cuộc chơi
        for(int iPlayer = 0; iPlayer < this.iNumOfPlayers; ++iPlayer)
        {
            this.lstPlayerCards.get(iPlayer).playThisCard(
                    lstCardInRound.get(iPlayer).getId()
                    );
        }
        

        return iResult;

    }

}
